home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Internet / Misc / ytalk / Source / main.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-12  |  15.8 KB  |  750 lines

  1. /* main.c - YTalk V2.0 main program */
  2.  
  3. /*               NOTICE
  4.  *
  5.  * Copyright (c) 1990 Britt Yenne.  All rights reserved.
  6.  * 
  7.  * This software is provided AS-IS.  The author gives no warranty,
  8.  * real or assumed, and takes no responsibility whatsoever for any 
  9.  * use or misuse of this software, or any damage created by its use
  10.  * or misuse.
  11.  * 
  12.  * This software may be freely copied and distributed provided that
  13.  * no part of this NOTICE is deleted or edited in any manner.
  14.  * 
  15.  */
  16.  
  17. /* Mail comments or questions to yenne@ccwf.cc.utexas.edu */
  18.  
  19. #include "ytalk.h"
  20. #include <signal.h>
  21. #include <sys/file.h>
  22. #include <sys/time.h>
  23. #include <sys/ioctl.h>
  24. #include <stdio.h>
  25. #ifdef POSIX
  26. #include <termios.h>
  27. #endif
  28.  
  29. #define PAUSE    16        /* seconds between housekeeping processing */
  30.  
  31. void yytalkabort();
  32.  
  33. extern char myhost[100];    /* caller's host name */
  34. extern int errno;        /* system error number */
  35. extern CTL_MSG msg;        /* for communication with talk daemon */
  36.  
  37. char *prog;            /* program name, ie: "ytalk" */
  38.  
  39. extern person p[MAXC];        /* those in the conversation */
  40. extern int pnum;        /* number of current conversationalists */
  41. extern int fdp[100];        /* for translating file nums to user nums */
  42.  
  43. char edit[4];        /* my four edit keys: RUB, WORD, KILL, & CLR */
  44. y_parm yparms;        /* my YTalk parameters */
  45. char errstr[100], estr[100];    /* temporary string storage */
  46. inptype inp;        /* for intermodule communication */
  47. long doalarm = 0;    /* set if there is housekeeping to do */
  48. long last_pulse = 0;    /* last housecleaning time */
  49. cpack crec;        /* for inter-YTalk communication */
  50. int conn = 0, sc = -1;    /* number of connected sockets */
  51. extern int autofd;    /* desc of auto-invite socket */
  52.  
  53. fd_set fdset;        /* main fdset for select() */
  54.  
  55. main(argc, argv)
  56. char **argv;
  57. {
  58.     int n, i;
  59.     fd_set sel;
  60.     char *c;
  61. #ifdef POSIX
  62.     struct termios tio;
  63. #else
  64.     struct sgttyb tty;
  65.     struct ltchars ltc;
  66. #endif
  67.     struct timeval tv;
  68.  
  69.     prog = argv[0];
  70.     if(argc < 2)
  71.     {
  72.     fprintf(stderr, "Usage: %s user[@hostname][#tty] [user...]\n", prog);
  73.     exit(2);
  74.     }
  75.  
  76.     init_socks();
  77.     init_term();
  78.     init_autoport();
  79.  
  80.     signal(SIGTERM, yytalkabort);
  81.     signal(SIGQUIT, yytalkabort);
  82.     signal(SIGINT,  yytalkabort);
  83.  
  84. #ifdef POSIX
  85.     tcgetattr(0, &tio);
  86.     RUB  = tio.c_cc[VERASE];
  87.     KILL = tio.c_cc[VKILL];
  88.     WORD = tio.c_cc[VWERASE];
  89. #else
  90.     ioctl(0, TIOCGETP, &tty);
  91.     ioctl(0, TIOCGLTC, (struct sgttyb *)<c);
  92.     RUB  = tty.sg_erase;
  93.     KILL = tty.sg_kill;
  94.     WORD = ltc.t_werasc;
  95. #endif
  96.     if(WORD == -1)
  97.     WORD = '\027';
  98.     CLR = '\024';
  99.     yparms.clr = CLR;
  100.  
  101.     FD_ZERO(&fdset);
  102.     FD_SET(0, &fdset);
  103.     for(argc--, argv++; argc; argc--, argv++)
  104.     newuser(*argv, 1);
  105.     do_auto();
  106.  
  107.     /* For housecleaning to occur every PAUSE seconds, we make our own
  108.      * little timer system.  SIGALRM is nice, but it interrupts system
  109.      * calls, which can be disastrous for certain areas of YTalk, so we
  110.      * affect the timer manually.
  111.      */
  112.  
  113.     last_pulse = time((time_t *)0);  /* don't use SIGALRM - make own timer */
  114.  
  115.     for(;;)
  116.     {
  117.     if(pnum == 0)    /* Exit YTalk if all conversations have terminated */
  118.         break;
  119.     if(conn != sc)
  120.     {
  121.         /* Count the number of currently active conversations, if
  122.          * we know it has changed.
  123.          */
  124.         for(n = 0, conn = 0; n < pnum; n++)
  125.         if(p[n].flags & P_CONNECT)
  126.             conn++;
  127.         if(conn == 0)
  128.         {
  129.         if(sc != -1)
  130.             w_clr(0);
  131.         for(n = 0; n < pnum; n++)
  132.             if(!(p[n].flags & P_CONNECT))
  133.             {
  134.             showstr(0, "Waiting for ");
  135.             showstr(0, p[n].name);
  136.             showstr(0, "@");
  137.             showstr(0, p[n].host);
  138.             showstr(0, " to respond\n");
  139.             }
  140.         }
  141.         /* Keep a saved value of 'conn', so we know when the
  142.          * number of active conversations changes.
  143.          */
  144.         sc = conn;
  145.     }
  146.     sel = fdset;
  147.     tv.tv_sec = 2L;    /* max 2 sec wait in select(), for timer purposes */
  148.     tv.tv_usec = 0L;
  149.     if((n = select(FD_SETSIZE, &sel, 0, 0, &tv)) < 0)
  150.     {
  151.         if(errno == EINTR)
  152.         continue;
  153.         panic("Select failed!");
  154.         continue;
  155.     }
  156.  
  157.     if(time((time_t *)0) - last_pulse > PAUSE)    /* Check timer */
  158.         sigalrm();
  159.  
  160.     if(!n)
  161.         continue;
  162.  
  163.     for(i = 0; n && i < FD_SETSIZE; i++)
  164.         if(FD_ISSET(i, &sel))
  165.         {
  166.         take_input(i);
  167.         n--;
  168.         }
  169.     }
  170.     yytalkabort(0);
  171. }
  172.  
  173. #define MAXBUF 64
  174. char buf[MAXBUF];
  175.  
  176. /* take_input() determines the nature of input on a file descriptor, and
  177.  * processes the input accordingly.
  178.  */
  179. take_input(fd)
  180. {
  181.     register int n;
  182.     register char *c;
  183.     int i, socklen;
  184.     struct sockaddr_in temp;
  185.  
  186.     if(fd == 0)        /* Keyboard input */
  187.     {
  188.     if((i = n = read(fd, buf, 1)) <= 0)    /* take it 1 char at a time */
  189.         yytalkabort(5);
  190.     if(*buf == 27)    /* ESC == give menu */
  191.     {
  192.         if(showmenu() == 0)
  193.         switch(inp.code)
  194.         {
  195.             case 'a':    /* add a new user */
  196.             newuser(inp.name, 1);
  197.             break;
  198.             case 'd':    /* delete a user */
  199.             killuser(inp.name);
  200.             break;
  201.             case 'o':    /* output a user */
  202.             outfile(inp.name, inp.data);
  203.             break;
  204.             default:
  205.             sprintf(errstr, "Unknown command: '%c'", inp.code);
  206.             errno = 0;
  207.             panic(errstr);
  208.         }
  209.     }
  210.     else if(*buf == 12)    /* ctrl-L */
  211.         redraw();
  212.     else
  213.     {
  214.         if(*buf == 13)
  215.         *buf = '\n';    /* some RAW curses progs won't do this */
  216.         xmit_char(*buf);
  217.     }
  218.     }
  219.     else if(fd == autofd)    /* input on auto-invite socket */
  220.     {
  221.     socklen = sizeof(struct sockaddr_in);
  222.     if((fd = accept(autofd, (struct sockaddr *) &temp, &socklen)) == -1)
  223.     {
  224.         panic("Cannot accept auto-socket connection");
  225.         return;
  226.     }
  227.     errno = 0;
  228.     n = read(fd, &crec, CSIZ);
  229.     close(fd);
  230.     if(n != CSIZ || crec.code != AUTO)
  231.     {
  232.         panic("Bad input on auto-invite socket");
  233.         return;
  234.     }
  235.     sprintf(estr, "Talk with %s@%s?", crec.name, crec.host);
  236.     putc('\07', stderr);
  237.     if(yes_no(estr) == 'y')
  238.     {
  239.         sprintf(estr, "%s@%s", crec.name, crec.host);
  240.         newuser(estr, 0);
  241.     }
  242.     }
  243.     else    /* input on a user's socket */
  244.     {
  245.     i = fdp[fd];
  246.     if(i < 0 || i >= pnum)
  247.     {
  248.         errno = 0;
  249.         panic("Input from unknown socket!\n");
  250.         close(fd);
  251.         return;
  252.     }
  253.  
  254.     /* If we have not already connected to or exchanged edit keys
  255.      * with this user, then do so.
  256.      */
  257.  
  258.     if(!(p[i].flags & P_CONTACT))
  259.     {
  260.         if(!(p[i].flags & P_CONNECT))
  261.         {
  262.         send_dgram(myhost, DELETE, i);
  263.         socklen = sizeof(struct sockaddr_in);
  264.         if((n = accept(p[i].sfd,(struct sockaddr *) &temp, &socklen)) == -1)
  265.         {
  266.             panic("Cannot accept connection");
  267.             FD_CLR(p[i].sfd, &fdset);
  268.             close(p[i].sfd);
  269.             return;
  270.         }
  271.         close(p[i].sfd);
  272.         FD_CLR(p[i].sfd, &fdset);
  273.         fd = p[i].sfd = n;
  274.         FD_SET(n, &fdset);
  275.         fdp[n] = i;
  276.         bcopy(edit, errstr, 3);
  277.         errstr[0] = RUBDEF;
  278.         write(n, errstr, 3);    /* send the edit keys */
  279.         p[i].flags |= P_CONNECT;
  280.         }
  281.         errno = 0;
  282.         if((n = read(fd, p[i].edit, 3)) != 3)
  283.         {
  284.         panic("Unknown contact");
  285.         FD_CLR(p[i].sfd, &fdset);
  286.         close(p[i].sfd);
  287.         return;
  288.         }
  289.         if(p[i].edit[0] == RUBDEF)
  290.         {
  291.         p[i].flags |= P_YTALK;
  292.         errno = 0;
  293.         if(write(fd, &yparms, YPARMLEN) != YPARMLEN)
  294.         {
  295.             panic("Cannot write to YTALK contact");
  296.             FD_CLR(p[i].sfd, &fdset);
  297.             close(p[i].sfd);
  298.             return;
  299.         }
  300.         if((n = read(fd, &p[i].yparms, YPARMLEN)) != YPARMLEN)
  301.         {
  302.             panic("Unknown YTALK contact");
  303.             FD_CLR(p[i].sfd, &fdset);
  304.             close(p[i].sfd);
  305.             return;
  306.         }
  307.         p[i].CLR = p[i].yparms.clr;
  308.         }
  309.         else
  310.         {
  311.         p[i].CLR = 255;
  312.         p[i].WCOLS = 80;
  313.         p[i].WROWS = 24;
  314.         }
  315.         p[i].flags |= P_CONTACT;
  316.         errno = 0;
  317.         if((p[i].win = add_window(p[i].name)) < 0)
  318.         {
  319.         panic("Cannot add new window");
  320.         FD_CLR(p[i].sfd, &fdset);
  321.         close(p[i].sfd);
  322.         return;
  323.         }
  324.         putc('\07', stderr);
  325.         putc('\07', stderr);
  326.  
  327.         /* If we are connected to a YTalk host, send information about
  328.          * all current contacts so that the YTalk host can import them.
  329.          */
  330.  
  331.         if(p[i].flags & P_YTALK)
  332.         for(n = 0; n < pnum; n++)
  333.         {
  334.             if(n == i)
  335.             continue;
  336.             if(!(p[n].flags & (P_YTALK | P_CONTACT)))
  337.             continue;
  338.             crec.code = IMPORT;
  339.             strncpy(crec.name, p[n].name, NAMELEN);
  340.             strncpy(crec.host, p[n].host, HOSTLEN);
  341.             write(fd, &crec, CSIZ);
  342.         }
  343.         if(conn == 0)
  344.         w_clr(0);
  345.         conn++;
  346.         return;
  347.     }
  348.  
  349.     /* Take and display input from the user socket */
  350.  
  351.     if((n = read(fd, buf, MAXBUF)) < 0)
  352.     {
  353.         sprintf(buf, "Cannot read from %s", p[i].name);
  354.         panic(buf);
  355.         return;
  356.     }
  357.     if(n == 0)
  358.     {
  359.         /* Lost connection */
  360.         deluser(i);
  361.         conn--;
  362.         return;
  363.     }
  364.  
  365.     /* Included in the user socket input can be control packets,
  366.      * telling this YTalk host to perform various tasks.  We must
  367.      * filter out and remove these packets for processing.  All
  368.      * other normal characters get displayed on the screen.
  369.      */
  370.  
  371.     for(c = buf; n > 0; c++, n--)
  372.         if(*c < 0 && *c != RUBDEF)
  373.         {
  374.         /* process control packet */
  375.         crec.code = *c;
  376.         c++;
  377.         n--;
  378.         if(n >= CSIZ-1)
  379.         {
  380.             bcopy(c, ((char *)(&crec))+1, CSIZ-1);
  381.             n -= CSIZ-1;
  382.             c += CSIZ-1;
  383.         }
  384.         else
  385.         {
  386.             if(n > 0)
  387.             bcopy(c, ((char *)(&crec))+1, n);
  388.             read(fd, ((char *)(&crec))+n+1, CSIZ-n-1);
  389.             n = 0;
  390.         }
  391.         process_pack(i);
  392.         n++;
  393.         c--;
  394.         }
  395.         else
  396.         {
  397.         /* process normal character */
  398.         process_char(p[i].win, *c, p[i].edit);
  399.  
  400.         /* If this user is being outputted, then update the
  401.          * output file.
  402.          */
  403.  
  404.         if(p[i].ffd != -1)
  405.             if(write(p[i].ffd, c, 1) != 1)
  406.             {
  407.             panic("Write to output file failed");
  408.             close(p[i].ffd);
  409.             p[i].ffd = -1;
  410.             }
  411.         }
  412.     }
  413. }
  414.  
  415. /* xmit_char() processes a keyboard char and transmits it to all hosts.
  416.  */
  417. xmit_char(ch)
  418. char ch;
  419. {
  420.     register int n;
  421.  
  422.     if(conn)
  423.     if(process_char(0, ch, edit) != 0)
  424.         return -1;
  425.     if(ch == RUB)
  426.     ch = RUBDEF;
  427.     for(n = 0; n < pnum; n++)    /* output char to all users */
  428.     {
  429.     if(!(p[n].flags & P_CONNECT))
  430.         continue;
  431.     if(ch == CLR && !(p[n].flags & P_YTALK))
  432.         write(p[n].sfd, "\n\n", 2);
  433.     else
  434.         write(p[n].sfd, &ch, 1);
  435.     }
  436.     return 0;
  437. }
  438.  
  439. /* process_pack will interpret a control packet and perform whatever
  440.  * function is required.
  441.  */
  442. process_pack(i)
  443. {
  444.     int n;
  445.  
  446.     switch(crec.code)
  447.     {
  448.     case IMPORT:    /* Prompt user if he wishes to import someone */
  449.         /* Don't import ourself or anyone we are already
  450.          * talking with.
  451.          */
  452.         if(strncmp(crec.name, msg.l_name, NAMELEN) == 0)
  453.         break;
  454.         for(n = 0; n < pnum; n++)
  455.         if(strncmp(crec.name, p[n].name, NAMELEN) == 0)
  456.             break;
  457.         if(n < pnum)
  458.         break;
  459.         sprintf(estr, "Import %s@%s?", crec.name, crec.host);
  460.         if(yes_no(estr) == 'y')
  461.         {
  462.         sprintf(estr, "%s@%s", crec.name, crec.host);
  463.         newuser(estr, 0);
  464.         crec.code = EXPORT;
  465.         write(p[i].sfd, &crec, CSIZ);
  466.         }
  467.         break;
  468.     case EXPORT:    /* Tell one of our clients to connect with another */
  469.         for(n = 0; n < pnum; n++)
  470.         {
  471.         if(strncmp(crec.name, p[n].name, NAMELEN) != 0)
  472.             continue;
  473.         if(crec.host[0] != '\0')
  474.             if(strncmp(crec.host, p[n].host, HOSTLEN) != 0)
  475.             continue;
  476.         break;
  477.         }
  478.         if(n >= pnum)
  479.         break;
  480.         crec.code = ACCEPT;
  481.         strncpy(crec.name, p[i].name, NAMELEN);
  482.         strncpy(crec.host, p[i].host, HOSTLEN);
  483.         if(p[n].flags & P_YTALK)
  484.         write(p[n].sfd, &crec, CSIZ);
  485.         break;
  486.     case ACCEPT:    /* Connect with another user */
  487.         sprintf(estr, "%s@%s", crec.name, crec.host);
  488.         newuser(estr, 0);
  489.         break;
  490.     }
  491. }
  492.  
  493. /* process_char interprets and displays input characters for each user.
  494.  * If the input character is a special edit key, then perform whatever
  495.  * editing function is required.
  496.  */
  497. process_char(w, ch, edit)
  498. char ch, *edit;
  499. {
  500.     if(ch == RUB)
  501.     w_rub(w);
  502.     else if(ch == KILL)
  503.     w_kill(w);
  504.     else if(ch == WORD)
  505.     w_word(w);
  506.     else if(ch == CLR)
  507.     w_clr(w);
  508.     else if(ch == 12)    /* ctrl-L */
  509.     return 0;
  510.     else
  511.     return add_char(w, ch);
  512.     return 0;
  513. }
  514.  
  515. /* annouce() will announce our intention to talk to the user defined
  516.  * in the msg structure.  First we check for an auto-invitation.  If
  517.  * there isn't one, ring the user.
  518.  */
  519. announce(hishost, i)
  520. char *hishost;
  521. int i;    /* user number */
  522. {
  523.     int n, fd, ring = 1;
  524.     char sav[NAME_SIZE];
  525.  
  526.     if(p[i].flags & P_CONNECT)    /* Don't ring a connected user */
  527.     {
  528.     sprintf(errstr, "Cannot ring %s: already connected", p[i].name);
  529.     errno = 0;
  530.     panic(errstr);
  531.     return 1;
  532.     }
  533.     if(conn == 0)
  534.     {
  535.     showstr(0, "Ringing ");
  536.     showstr(0, p[i].name);
  537.     showstr(0, "@");
  538.     showstr(0, p[i].host);
  539.     showstr(0, "...");
  540.     }
  541.     strncpy(sav, msg.l_name, NAME_SIZE);
  542.     strncpy(msg.l_name, "+AUTO", NAME_SIZE);
  543.     msg.r_tty[0] = '\0';
  544.     while((n = send_dgram(hishost, LOOK_UP, i)) == 0)
  545.     {
  546.     p[i].flags = 0;
  547.     if((fd = connect_to(-1)) < 0)
  548.     {
  549.         if(fd == -2)
  550.         {
  551.         send_dgram(hishost, DELETE, i);
  552.         continue;
  553.         }
  554.         strncpy(msg.l_name, sav, NAME_SIZE);
  555.         if(conn == 0)
  556.         showstr(0, "\n");
  557.         return -1;
  558.     }
  559.     crec.code = AUTO;
  560.     strncpy(crec.name, sav, NAME_SIZE);
  561.     crec.name[NAMELEN-1] = '\0';
  562.     strncpy(crec.host, myhost, HOSTLEN);
  563.     write(fd, &crec, CSIZ);
  564.     close(fd);
  565.     ring = 0;
  566.     break;
  567.     }
  568.  
  569.     strncpy(msg.l_name, sav, NAME_SIZE);
  570.     strncpy(msg.r_tty, p[i].tty, TTY_SIZE);
  571.     p[i].flags = 0;
  572.  
  573.     while(ring)
  574.     if((n = send_dgram(hishost, ANNOUNCE, i)) == 0)
  575.         break;
  576.     else
  577.     {
  578.         if(n == 1 && p[i].tty[0] != '\0')
  579.         {
  580.         /* If the user specified a TTY, but that user
  581.          * is not logged into that TTY, try looking him
  582.          * up on any TTY before giving up.
  583.          */
  584.         p[i].tty[0] = '\0';
  585.         msg.r_tty[0] = '\0';
  586.         continue;
  587.         }
  588.         show_error(n, p[i].name, hishost);
  589.         if(conn == 0)
  590.         showstr(0, "\n");
  591.         return -1;
  592.     }
  593.     if(conn == 0)
  594.     showstr(0, "\n");
  595.     return 0;
  596. }
  597.  
  598. /* Update the automatic communications port.
  599.  */
  600. do_auto()
  601. {
  602.     int n;
  603.  
  604.     if(autofd == -1)
  605.     return;
  606.     if((n = send_auto(LEAVE_INVITE)) != 0)
  607.     {
  608.     show_error(n, "AUTO", myhost);
  609.     FD_CLR(autofd, &fdset);
  610.     close(autofd);
  611.     autofd = -1;
  612.     }
  613.     FD_SET(autofd, &fdset);
  614. }
  615.  
  616. /* panic() displays any errors in a user-friendly way, if possible.
  617.  */
  618. panic(str)
  619. char *str;
  620. {
  621.     int e;
  622.  
  623.     e = errno;
  624.     if(showerror(str, e) == 0)
  625.     return;
  626.     close_term();
  627.     if(e)
  628.     {
  629.     errno = e;
  630.     perror(str);
  631.     }
  632.     else
  633.     fprintf(stderr, "%s\n", str);
  634.     sleep(3);
  635.     yytalkabort(5);
  636. }
  637.  
  638. char *errs[] = {
  639.     "No error (?)",
  640.     "Not logged in",
  641.     "Operation failed in daemon (?)",
  642.     "Caller's machine unknown to daemon",
  643.     "Not accepting messages",
  644.     "Unknown request to daemon",
  645.     "Version mismatch error",
  646.     "Invalid TCP socket address",
  647.     "Invalid UDP socket address"
  648. };
  649.  
  650. show_error(n, name, host)
  651. char *name, *host;
  652. {
  653.     if(n == -1)
  654.     return;
  655.     if(n >= 0 && n < 9)
  656.     sprintf(errstr, "%s@%s: %s", name, host, errs[n]);
  657.     else
  658.     sprintf(errstr, "%s@%s: Unknown error in daemon", name, host);
  659.     if(showerror(errstr, 0) == 0)
  660.     return;
  661.     close_term();
  662.     fprintf(stderr, "%s\n", errstr);
  663.     sleep(3);
  664.     yytalkabort(1);
  665. }
  666.  
  667. /* showstr() displays a string on the terminal.
  668.  */
  669. showstr(w, str)
  670. char *str;
  671. {
  672.     for(; *str; str++)
  673.     add_char(w, *str);
  674. }
  675.  
  676. /* sigalrm performs housecleaning, including resending the auto-invitation
  677.  * to make sure the daemon doesn't time it out, and re-ringing any
  678.  * unconnected parties.
  679.  */
  680. sigalrm()
  681. {
  682.     long t;
  683.     int n, e, found = 0;
  684.  
  685.     if(autofd != -1)
  686.     do_auto();
  687.  
  688.     t = time((time_t *)0);
  689.     if(doalarm == 0)
  690.     {
  691.     last_pulse = t;
  692.     return;
  693.     }
  694.     for(n = 0; n < pnum; n++)
  695.     if(!(p[n].flags & P_CONNECT))
  696.     {
  697.         if(!found)
  698.         found = 1;
  699.         if(t - p[n].last_ring < RING_WAIT)
  700.         continue;
  701.         p[n].last_ring = t;
  702.         strncpy(msg.r_name, p[n].name, NAME_SIZE);
  703.         strncpy(msg.r_tty, "", TTY_SIZE);
  704.         (void) send_dgram(myhost, LEAVE_INVITE, n);
  705.         sprintf(errstr, "Rering %s@%s?", p[n].name, p[n].host);
  706.         if(found == 1)
  707.         {
  708.         putc('\07', stderr);
  709.         found = 2;
  710.         }
  711.         if(yes_no(errstr) == 'y')
  712.         {
  713.         if(announce(p[n].host, n) != 0)
  714.         {
  715.             send_dgram(myhost, DELETE, n);
  716.             deluser(n);
  717.             n--;
  718.         }
  719.         }
  720.         else
  721.         {
  722.         sprintf(errstr, "DELETE %s@%s?", p[n].name, p[n].host);
  723.         if(yes_no(errstr) == 'y')
  724.         {
  725.             send_dgram(myhost, DELETE, n);
  726.             deluser(n);
  727.             n--;
  728.         }
  729.         }
  730.     }
  731.     if(!found)
  732.     doalarm = 0;
  733.     last_pulse = time((time_t *)0);
  734. }
  735.  
  736. /* Finally, yytalkabort() terminates YTalk on error or sucessful completion.
  737.  */
  738. void yytalkabort(n)
  739. {
  740.     int x;
  741.  
  742.     close_term();
  743.     for(x = 3; x < 32; x++)
  744.     {
  745.     shutdown(x, 2);
  746.     close(x);
  747.     }
  748.     exit(n);
  749. }
  750.